/**
 *    Copyright 2010 Merrick Zhang ( anphorea@gmail.com )
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */
package anpho.proxyvalidator.proc;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;

import anpho.proxyvalidator.bean.IConstants;
import anpho.proxyvalidator.bean.ProxyBean;
import anpho.proxyvalidator.config.AppContext;

/**
 * @author Merrick
 * 
 */
public abstract class AValidator {
	final static int DEFAULT_READ_TIMEOUT = 5000;

	final static int DEFAULT_CONN_TIMEOUT = 5000;

	private static final String URL = AppContext.getStr("URL");

	private static final String TOKEN = AppContext.getStr("STR");

	/**
	 * Thread Vector.
	 */
	Vector<Thread> vPool = new Vector<Thread>();

	/**
	 * Proxies to be validated
	 */
	Set<Proxy> poolIn = new HashSet<Proxy>();

	/**
	 * Proxies have been validated
	 */
	Set<ProxyBean> poolOut = new HashSet<ProxyBean>();

	/**
	 * Registered CallBack Handlers
	 */
	HashSet<IValidatorCallBack> callBackHandlers = new HashSet<IValidatorCallBack>();

	/**
	 * get the current result set.maybe not complete.
	 * 
	 * @return
	 */
	public Set<ProxyBean> getCurrentResults() {
		return poolOut;
	}

	/**
	 * When Job's completed,call this method to notify all the CallBackHandlers.
	 * 
	 * @param objects
	 */
	void notifyCallBackHanders(Object... objects) {
		Iterator<IValidatorCallBack> i = callBackHandlers.iterator();
		while (i.hasNext()) {
			i.next().callback(objects);
		}
	}

	/**
	 * This method is used for the other classes to register as
	 * CallBackHandlers.
	 * 
	 * @param cb
	 */
	public void registerCallback(IValidatorCallBack cb) {
		callBackHandlers.add(cb);
	}

	/**
	 * You'd better use registerCallback to register first.Then when the
	 * validate work is done,you'll be notified. This method will call
	 * start(DEFAULT_THREAD) so you don't need to call start() manually.
	 * 
	 * @param p
	 */
	public abstract void validate(Set<Proxy> p);

	/**
	 * try to fetch the specified URL via the given Proxy.if token
	 * appears,return the response time,otherwise return ProxyBean.UNACCESSABLE
	 * or ProxyBean.TIMEOUT
	 * 
	 * @param proxy
	 * @param url
	 * @param token
	 * @return
	 */
	public long validateProxy(Proxy proxy, String url, String token) {
		URL u;
		try {
			u = new URL(url);
		} catch (MalformedURLException e) {
			return IConstants.UNACCESSABLE;
		}
		HttpURLConnection uc = null;
		try {
			uc = (HttpURLConnection) u.openConnection(proxy);
		} catch (IOException e) {
			return IConstants.UNACCESSABLE;
		}

		// set timeout values.
		int timeout = AppContext.getInt("VALIDATE_READ_TIMEOUT");
		uc.setReadTimeout(timeout == 0 ? DEFAULT_READ_TIMEOUT : timeout);
		timeout = AppContext.getInt("VALIDATE_CONN_TIMEOUT");
		uc.setConnectTimeout(timeout == 0 ? DEFAULT_CONN_TIMEOUT : timeout);

		// record the start time
		long start_time = System.currentTimeMillis();
		long times = IConstants.TIMEOUT;
		try {
			uc.connect();
			StringBuffer sb = new StringBuffer();
			InputStream is = uc.getInputStream();
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			while (br.ready()) {
				sb.append(br.readLine());
			}
			br.close();
			is.close();
			if (sb.toString().contains(token)) {
				times = System.currentTimeMillis() - start_time;
			} else {
				times = IConstants.UNACCESSABLE;
			}
		} catch (IOException e) {
			return IConstants.TIMEOUT;
		}

		return times;
	}

	/**
	 * validate using the default url and token.
	 * 
	 * @param proxy
	 * @return
	 */
	public long validateProxy(Proxy proxy) {
		return validateProxy(proxy, URL, TOKEN);
	}
}
